using System;
using System.Linq;
using Caliburn.Micro;

namespace MediaOwl.Common.ScreenManagement
{
    /// <summary>
    /// An <see cref="IResult"/>, that takes a Type of ChildScreen, 
    /// locates/instanciates it (<see cref="locateChild"/>), 
    /// locates its Parent (<see cref="In&lt;TParent&gt;"/>, 
    /// configures the ChildScreen (<see cref="Configured"/>) 
    /// and activates it in its Parent.
    /// </summary>
    /// <remarks>This class was taken from the Caliburn Framework</remarks>
    /// <typeparam name="TChild">The Child-Type</typeparam>
    public class OpenChildResult<TChild> : IResult 
        where TChild : class
    {
        private Func<ActionExecutionContext, IConductor> locateParent;
        private Action<TChild> onConfigure;
        private readonly string screenId;

        private readonly Func<ActionExecutionContext, TChild> locateChild =
            c => IoC.Get<TChild>();

        public OpenChildResult(string screenId = null)
        {
            this.screenId = screenId;
        }

        public OpenChildResult(TChild child, string screenId = null)
        {
            this.screenId = screenId;
            locateChild = c => child;
        }

        public OpenChildResult<TChild> In<TParent>()
        {
            locateParent = c => IoC.Get<TParent>() as IConductor;
            return this;
        }

        public OpenChildResult<TChild> In(object parent)
        {
            locateParent = c => parent as IConductor;
            return this;
        }

        public OpenChildResult<TChild> Configured(Action<TChild> configure)
        {
            onConfigure = configure;
            return this;
        }

        public void Execute(ActionExecutionContext context)
        {
            if (locateParent == null)
                locateParent = c => (IConductor)c.Target;

            var parent = locateParent(context);

            var items = parent.GetChildren().OfType<TChild>();
            bool childIsNew = false;
            TChild child = null;
            if (items.Count() > 0 && !string.IsNullOrWhiteSpace(screenId))
            {
                child = items.FirstOrDefault(x =>
                {
                    var wi = x as IWorkitem;
                    return wi != null && wi.ScreenId == wi.GetType().Name + screenId;
                });
            }

            if (child == null)
            {
                childIsNew = true;
                child = locateChild(context);

                if (child is IWorkitem)
                    ((IWorkitem)child).ScreenId = child.GetType().Name + screenId;
            }

            if (child is IScreenExtensions)
            {
                ((IScreenExtensions) child).ViewInitiallyLoaded = o =>
                                                             {
                                                                 if (childIsNew && onConfigure != null)
                                                                     onConfigure(child);
                                                             };
                parent.ActivateItem(child);
            }
            else
            {
                parent.ActivateItem(child);
                if (childIsNew && onConfigure != null)
                    onConfigure(child);
            }

            Completed(this, new ResultCompletionEventArgs());
        }

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    }
}